package com.hancher.gamelife2.backup;

import static com.hancher.gamelife2.backup.BaiduNetManager.BAIDU_API_UPLOAD_MESSAGE_FINISHED;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;

import androidx.navigation.Navigation;
import androidx.sqlite.db.SimpleSQLiteQuery;

import com.csvreader.CsvReader;
import com.csvreader.CsvWriter;
import com.hancher.common.androidutil.AsyncUtils;
import com.hancher.common.androidutil.FileChangeNotifityUtil;
import com.hancher.common.androidutil.LogUtil;
import com.hancher.common.androidutil.MmkvUtil;
import com.hancher.common.androidutil.PathUtil;
import com.hancher.common.androidutil.ToastUtil;
import com.hancher.common.base.BaseMvpFragment;
import com.hancher.common.javautil.Base64Util;
import com.hancher.common.javautil.ByteStrUtil;
import com.hancher.common.javautil.EmptyUtil;
import com.hancher.common.javautil.FileUtil;
import com.hancher.common.javautil.GsonUtil;
import com.hancher.common.javautil.StringUtil;
import com.hancher.common.javautil.ZipUtil;
import com.hancher.gamelife2.R;
import com.hancher.gamelife2.databinding.FragmentBackupBinding;
import com.hancher.gamelife2.home.HomePresenter;
import com.hancher.gamelife2.login.BaiduLoginFragment;
import com.hancher.gamelife2.main.MainDatabase;
import com.hancher.gamelife2.main.MainDatabase_Impl;
import com.hjq.permissions.Permission;
import com.hjq.permissions.XXPermissions;
import com.jeremyliao.liveeventbus.LiveEventBus;
import com.lxj.xpopup.XPopup;

import java.io.File;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;

import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.functions.Consumer;

/**
 * 作者 : Hancher ytu_shaoweijie@163.com <br/>
 * 时间 : 2023/3/12 0012 22:49 <br/>
 * 描述 :
 */
public class BackupFragment extends BaseMvpFragment<FragmentBackupBinding, HomePresenter> {

    private Disposable d;

    @Override
    protected void initView(Bundle savedInstanceState) {

    }

    @Override
    protected void initListener() {
        LiveEventBus.get(BaiduNetManager.BAIDU_API_UPLOAD_MESSAGE, String.class)
                .observe(this, s -> {
                    binding.tvUpload.setText("上传：" + s);

                    if (BAIDU_API_UPLOAD_MESSAGE_FINISHED.equals(s)) {
                        binding.tvOk.setVisibility(View.VISIBLE);
                        binding.tvCancel.setVisibility(View.GONE);
                        binding.imgBackuping.clearAnimation();
                    }
                });
        LiveEventBus.get(BaiduNetManager.BAIDU_API_DOWNLOAD_MESSAGE, String.class)
                .observe(this, s -> {
                    binding.tvDownload.setText("下载：" + s);

                    if ("下载完成".equals(s)) {
                        mergeData();
                    } else if (EmptyUtil.isNotEmpty(s) && s.startsWith("下载失败")) {
                        generateCsv();
                    }
                });
        binding.tvOk.setOnClickListener(v -> {
            Navigation.findNavController(binding.container).popBackStack();
        });
        binding.tvCancel.setOnClickListener(v -> {
            if (d != null && !d.isDisposed()) {
                d.dispose();
            }
            Navigation.findNavController(binding.container).popBackStack();
        });
    }

    @Override
    protected void initData() {

    }

    @Override
    public void onResume() {
        super.onResume();
        // 检查百度网盘权限
        long expireTime = MmkvUtil.getLong(BaiduLoginFragment.BAIDUNET_EXPIRATION_TIME, 0L);
        if (System.currentTimeMillis() > expireTime) {
            new XPopup.Builder(getContext()).asConfirm("数据备份到百度网盘", "本人太穷，没有前购买服务器，因此利用百度网盘备份数据，点击确定进行登录百度网盘账号，谢谢理解。",
                            () -> Navigation.findNavController(binding.container).navigate(R.id.action_backupFragment_to_baiduLoginFragment))
                    .show();
            return;
        }

        XXPermissions.with(this)
                .permission(Permission.WRITE_EXTERNAL_STORAGE,
                        Permission.READ_MEDIA_IMAGES,
                        Permission.READ_MEDIA_VIDEO,
                        Permission.READ_MEDIA_AUDIO)
                .request((permissions, allGranted) -> {
                    if (allGranted) {
                        downFromBaidu();
                    } else {
                        ToastUtil.show("权限未全部同意！");
                    }
                });
    }


    //region 同步数据
    //表
    final List<String> tables = Arrays.asList("matter", "function", "diary", "position", "weather", "birthday", "account");
    //备份路径
    String path = PathUtil.OUT_APP_CACHE + File.separator + "backup" + File.separator;
    //压缩包路径
    String zipPath = PathUtil.OUT_APP_CACHE + File.separator + "GameLife2Backup.zip";
    String dbPath = PathUtil.IN_DB + File.separator + MainDatabase.DATABASE_NAME;
    String destBaiduPath = BaiduNetApi.BDPAN_BACKUP_PATH + "GameLife2Backup.zip";
    //密码
    final String psd = "Hancher";
    public static final String BASE64_START = "[[BASE64]]";


    /**
     * 下载压缩包
     */
    private void downFromBaidu() {
        if (binding.tvOk.getVisibility() == View.GONE) {
            return;
        }
        binding.tvOk.setVisibility(View.GONE);
        binding.tvCancel.setVisibility(View.VISIBLE);
        // 动画旋转
        Animation animation = AnimationUtils.loadAnimation(getContext(), R.anim.rotate_animation);
        animation.setRepeatMode(Animation.INFINITE);
        animation.setRepeatCount(Animation.INFINITE);
        binding.imgBackuping.startAnimation(animation);

        d = AsyncUtils.run(emitter -> {
            FileUtil.deleteFile(zipPath);
            FileUtil.touchDir(path);
            BaiduNetManager.getInstance().downloadFile(destBaiduPath, zipPath);

        }, (Consumer<String>) data -> {

        });
    }

    /**
     * 合并数据
     */
    private void mergeData() {
        AsyncUtils.run(emitter -> {
            LiveEventBus.get(BaiduNetManager.BAIDU_API_DOWNLOAD_MESSAGE, String.class)
                    .post(String.format("开始解压"));
            ZipUtil.unZip(zipPath, PathUtil.OUT_APP_CACHE, psd);

            LiveEventBus.get(BaiduNetManager.BAIDU_API_DOWNLOAD_MESSAGE, String.class)
                    .post(String.format("解压完成，开始合并"));

            MainDatabase_Impl.getInstance().close();

            File backupDir = new File(path);
            String[] csvs = backupDir.list();
            SQLiteDatabase innerDb = SQLiteDatabase.openOrCreateDatabase(dbPath, null);
            int insertCount = 0, updateCount = 0;
            for (int i = 0; i < csvs.length; i++) {
                if (EmptyUtil.isEmpty(csvs[i])) {
                    continue;
                }
                LiveEventBus.get(BaiduNetManager.BAIDU_API_DOWNLOAD_MESSAGE, String.class)
                        .post(String.format("合并文件：" + csvs[i]));
                CsvReader csvReader = null;
                try {
                    File csv = new File(path, csvs[i]);
                    String table = csvs[i].replace(".csv", "");
                    csvReader = new CsvReader(csv.getAbsolutePath());
                    csvReader.readRecord();
                    String[] headers = csvReader.getValues();
                    int updateTimeIndex = -1;
                    int uuidIndex = -1;
                    for (int j = 0; j < headers.length; j++) {
                        if ("updateTime".equals(headers[j])) {
                            updateTimeIndex = j;
                        }
                        if ("uuid".equals(headers[j])) {
                            uuidIndex = j;
                        }
                    }
                    if (updateTimeIndex == -1) {
                        LogUtil.d("此表无updateTime，不进行更新");
                        continue;
                    }
                    if (uuidIndex == -1) {
                        LogUtil.d("此表无uuid，不进行更新");
                        continue;
                    }


                    try {
                        innerDb.beginTransaction();//开始事务机制
                        while (csvReader.readRecord()) {
                            String[] values = csvReader.getValues();
                            String uuid = Base64Util.decode(csvReader.get(uuidIndex).substring(BASE64_START.length()));

                            //判断是否需要更新
                            String[] columns = new String[]{"count(*)"};
                            String selection = " uuid=? AND updateTime<? ";
                            String[] selectionArgs = new String[]{uuid, csvReader.get(updateTimeIndex)};
                            Cursor cursor = innerDb.query(table, columns, selection, selectionArgs, null, null, null, null);
                            cursor.moveToFirst();
                            boolean needUpdate = cursor.getInt(0) > 0;
                            cursor.close();

                            //判断是否需要插入
                            columns = new String[]{"count(*)"};
                            selection = " uuid=? ";
                            selectionArgs = new String[]{uuid};
                            cursor = innerDb.query(table, columns, selection, selectionArgs, null, null, null, null);
                            cursor.moveToFirst();
                            boolean needInsert = cursor.getInt(0) < 1;
                            cursor.close();


                            if (!needInsert && !needUpdate) {
                                continue;
                            }
                            LogUtil.d("当前需要插入更新：" + needInsert + " " + needUpdate);

                            Cursor c = innerDb.rawQuery("PRAGMA table_info(" + table + ")", null);
                            HashMap<String, String> typeMap = new HashMap<>();
                            if (c.moveToFirst()) {
                                do {
//                                    System.out.println("name: " + c.getString(1) + " type: " + c.getString(2));
                                    for (int j = 0; j < cursor.getColumnCount(); j++) {
                                        typeMap.put(c.getString(1),
                                                c.getString(2));
                                    }
                                } while (c.moveToNext());
                            }
                            c.close();

                            HashMap<String, String> logMap = new HashMap<>();
                            ContentValues cv = new ContentValues();
                            for (int j = 0; j < values.length; j++) {
                                if ("id".equals(headers[j])) {
                                    continue;
                                } else if (typeMap.get(headers[j]) == null) {
                                    continue;
                                } else if (EmptyUtil.isEmpty(values[j])) {
                                    continue;
//                                } else if (typeMap.get(headers[j]) == Cursor.FIELD_TYPE_NULL) {
//                                    continue;
                                } else if ("INTEGER".equals(typeMap.get(headers[j]))) {
                                    try {
                                        cv.put(headers[j], new BigDecimal(values[j]).longValue());
                                    } catch (Exception e) {
                                        LogUtil.d("Long转换失败" + values[j] + e);
                                        continue;
                                    }
                                } else if ("REAL".equals(typeMap.get(headers[j]))) {
                                    try {
                                        cv.put(headers[j], new BigDecimal(values[j]).doubleValue());
                                    } catch (Exception e) {
                                        LogUtil.d("Double转换失败" + values[j] + e);
                                        continue;
                                    }
                                } else if ("TEXT".equals(typeMap.get(headers[j]))) {
                                    if (EmptyUtil.isNotEmpty(values[j]) && values[j].startsWith(BASE64_START)) {
                                        cv.put(headers[j], Base64Util.decode(values[j].substring(BASE64_START.length())));
                                    } else {
                                        cv.put(headers[j], values[j]);
                                    }
                                } else if ("BLOB".equals(typeMap.get(headers[j]))) {
                                    cv.put(headers[j], ByteStrUtil.hexStringToByte(values[j]));
                                }
                                if (EmptyUtil.isNotEmpty(values[j]) && String.valueOf(values[j]).startsWith(BASE64_START)) {
                                    logMap.put(headers[j], Base64Util.decode(String.valueOf(values[j]).substring(BASE64_START.length())));
                                } else {
                                    logMap.put(headers[j], String.valueOf(values[j]));
                                }
                            }
                            if (needUpdate) {
                                // TODO: 2023/12/23 更新
                                LogUtil.d("执行update参数：" + GsonUtil.obj2JsonStr(logMap));
                                updateCount += innerDb.update(table, cv, "uuid=?", new String[]{uuid});
                            } else if (needInsert) {
                                // TODO: 2023/12/23 插入
                                LogUtil.d("执行insert参数：" + GsonUtil.obj2JsonStr(logMap));
                                insertCount += innerDb.insert(table, null, cv) > 0 ? 1 : 0;
                            }
                        }

                        innerDb.setTransactionSuccessful();//事务已经执行成功
                    } catch (Exception e) {
                        LogUtil.e("err:", e);
                        LiveEventBus.get(BaiduNetManager.BAIDU_API_DOWNLOAD_MESSAGE, String.class)
                                .post("合并语句异常：" + e);
                    } finally {
                        innerDb.endTransaction();//结束事务
                    }
                    csvReader.close();
                } catch (Exception e) {
                    LiveEventBus.get(BaiduNetManager.BAIDU_API_DOWNLOAD_MESSAGE, String.class)
                            .post("合并数据异常：" + e);
                } finally {
                    try {
                        if (csvReader != null) {
                            csvReader.close();
                        }
                    } catch (Exception e) {
                    }
                }
            }
            LiveEventBus.get(BaiduNetManager.BAIDU_API_DOWNLOAD_MESSAGE, String.class)
                    .post("合并文件结束, 插入" + insertCount + "条, 更新" + updateCount + "条");

            MainDatabase_Impl.restart();

            emitter.onNext("");
        }, (Consumer<String>) data -> {
            generateCsv();
        }, t -> {
            LiveEventBus.get(BaiduNetManager.BAIDU_API_DOWNLOAD_MESSAGE, String.class)
                    .post("解压或合并失败" + t);
            generateCsv();
        });
    }

    /**
     * 生成csv
     */
    private void generateCsv() {
        d = AsyncUtils.run(emitter -> {
            LogUtil.d("生成路径：" + path);
            for (String table : tables) {
                CsvWriter csvWriter = null;
                try {
                    // 文件
                    String fileName = path + table + ".csv";
                    FileUtil.deleteFile(fileName);
                    FileUtil.touchFile(fileName);
                    FileChangeNotifityUtil.notifySystemFileChange(fileName);
                    LogUtil.d("文件", fileName, "创建结果", new File(fileName).exists());
                    csvWriter = new CsvWriter(fileName);

                    // sql语句
                    String sql = " SELECT * FROM " + table + " ";
                    Cursor cursor = MainDatabase_Impl.getInstance().query(new SimpleSQLiteQuery(sql));

                    // head
                    int columnCount = cursor.getColumnCount();
                    List<String> columnNames = Arrays.asList(cursor.getColumnNames());
                    csvWriter.writeRecord(cursor.getColumnNames());
                    LogUtil.d("表头: " + columnNames);

                    // body
                    if (cursor.getCount() < 1) {
                        continue;
                    }
                    cursor.moveToFirst();
                    long index = 0;
                    do {
                        String[] row = new String[columnCount];
                        for (int i = 0; i < columnCount; i++) {
                            int type = cursor.getType(i);
                            if (type == Cursor.FIELD_TYPE_NULL) {
                                row[i] = "";
                            } else if (type == Cursor.FIELD_TYPE_INTEGER) {
                                row[i] = StringUtil.getObjStr(BigDecimal.valueOf(cursor.getLong(i)), "");
                            } else if (type == Cursor.FIELD_TYPE_FLOAT) {
                                row[i] = StringUtil.getObjStr(BigDecimal.valueOf(cursor.getFloat(i)), "");
                            } else if (type == Cursor.FIELD_TYPE_STRING) {
                                row[i] = Base64Util.encode(cursor.getString(i));
                                row[i] = BASE64_START + row[i];
                            } else if (type == Cursor.FIELD_TYPE_BLOB) {
                                row[i] = ByteStrUtil.byte2Str(cursor.getBlob(i));
                            } else {
                                row[i] = "";
                                LogUtil.e("err: 未知类型");
                            }
                        }
                        csvWriter.writeRecord(row);
                        if (index % 10 == 9) {
                            csvWriter.flush();
                        }
                        index++;
                    } while (cursor.moveToNext());
                    csvWriter.flush();
                } catch (Exception e) {
                    LogUtil.e("异常 当前表[" + table + "] :", e);
                } finally {
                    try {
                        if (csvWriter != null) {
                            csvWriter.close();
                        }
                    } catch (Exception e) {
                    }
                }
            }
            emitter.onNext("");
            emitter.onComplete();
        }, (Consumer<String>) data -> {
            LogUtil.d("生成csv完成");
            zipFile();
        });
    }

    /**
     * 压缩zip
     */
    private void zipFile() {
        AsyncUtils.run(emitter -> {
            FileUtil.deleteFile(zipPath);
            LogUtil.d("压缩开始");
            ZipUtil.zipFileUnderDir2File(path, zipPath, psd);
            FileUtil.deleteFloder(path);
            emitter.onNext("");
            emitter.onComplete();
        }, (Consumer<String>) data -> {
            LogUtil.d("压缩完成");
            uploadBaidu();
        });
    }

    /**
     * 上传百度网盘
     */
    private void uploadBaidu() {
        BaiduNetManager.getInstance().setDeleteCache(true);
        BaiduNetManager.getInstance().uploadFile(zipPath, destBaiduPath);
    }
    //endregion

//    @Override
//    public void onPause() {
//        binding.imgBackuping.clearAnimation();
//        super.onPause();
//    }
}